/*
 * contrib/hstore/hstore.h
 */
#ifndef __HSTORE_H__
#define __HSTORE_H__

#include "fmgr.h"
#include "utils/array.h"


/*
 * HEntry: hstore 中每个键 _和_ 值都有一个这样的结构
 *
 * 位置偏移指向 _结束_，这样我们可以通过从上一个条目中减去来获取长度。ISFIRST 标志让我们判断
 * 是否有上一个条目。
 */
typedef struct
{
	uint32		entry;
} HEntry;

#define HENTRY_ISFIRST 0x80000000
#define HENTRY_ISNULL  0x40000000
#define HENTRY_POSMASK 0x3FFFFFFF

/* 注意可能的多次评估，同时访问先前的数组元素 */
#define HSE_ISFIRST(he_) (((he_).entry & HENTRY_ISFIRST) != 0)
#define HSE_ISNULL(he_) (((he_).entry & HENTRY_ISNULL) != 0)
#define HSE_ENDPOS(he_) ((he_).entry & HENTRY_POSMASK)
#define HSE_OFF(he_) (HSE_ISFIRST(he_) ? 0 : HSE_ENDPOS((&(he_))[-1]))
#define HSE_LEN(he_) (HSE_ISFIRST(he_)	\
					  ? HSE_ENDPOS(he_) \
					  : HSE_ENDPOS(he_) - HSE_ENDPOS((&(he_))[-1]))

/*
 * 由 "endpos" 的大小决定（即 HENTRY_POSMASK），虽然这有点学术，因为当前的 varlenas
 * （因此输入和整个 hstore）具有相同的限制
 */
#define HSTORE_MAX_KEY_LEN 0x3FFFFFFF
#define HSTORE_MAX_VALUE_LEN 0x3FFFFFFF

typedef struct
{
	int32		vl_len_;		/* varlena 头部（请勿直接触摸！） */
	uint32		size_;			/* hstore 中的标志和项目数量 */
	/* HEntry 的数组紧随其后 */
} HStore;

/*
 * 不可能在一个 hstore 中放入超过 2^28 个项目，因此我们保留
 * 大小字段的顶部几位。请参阅 hstore_compat.c 了解原因之一。
 * 此处留出了一些位供将来使用。MaxAllocSize 使得实际计数限制稍微超过 2^28 / 3，
 * 或 INT_MAX / 24，对于充满 4 字节键和空值的 hstore 的限制。因此，我们
 * 不明确检查格式强加的限制。
 */
#define HS_FLAG_NEWVERSION 0x80000000

#define HS_COUNT(hsp_) ((hsp_)->size_ & 0x0FFFFFFF)
#define HS_SETCOUNT(hsp_,c_) ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION)


/*
 * "x" 来自现有的 HS_COUNT()（如讨论，<= INT_MAX/24）或
 * Pairs 数组长度（由于 MaxAllocSize，<= INT_MAX/40）。"lenstr" 不超过
 * INT_MAX，那个极端情况出现于 hstore_from_arrays()。
 * 因此，这个计算的限制大约为 INT_MAX / 5 + INT_MAX。
 */
#define HSHRDSIZE	(sizeof(HStore))
#define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) )

/* 注意 x 的多次评估 */
#define ARRPTR(x)		( (HEntry*) ( (HStore*)(x) + 1 ) )
#define STRPTR(x)		( (char*)(ARRPTR(x) + HS_COUNT((HStore*)(x)) * 2) )

/* 注意多次/未评估 */
#define HSTORE_KEY(arr_,str_,i_)	((str_) + HSE_OFF((arr_)[2*(i_)]))
#define HSTORE_VAL(arr_,str_,i_)	((str_) + HSE_OFF((arr_)[2*(i_)+1]))
#define HSTORE_KEYLEN(arr_,i_)		(HSE_LEN((arr_)[2*(i_)]))
#define HSTORE_VALLEN(arr_,i_)		(HSE_LEN((arr_)[2*(i_)+1]))
#define HSTORE_VALISNULL(arr_,i_)	(HSE_ISNULL((arr_)[2*(i_)+1]))

/*
 * 目前，以下这些宏是 _唯一_ 依赖于 HEntry 内部知识的地方。
 * 其他的都应该使用上述宏。例外：hstore_compat.c 中的就地升级
 * 直接干扰条目。
 */

/*
 * 将一个键/值对（必须从 sptr_ 开始连续）复制到正在构建的 hstore 中；
 * dent_ 是一个 HEntry*，dbuf_ 是目标的字符串缓冲区，dptr_ 是当前
 * 在目标中的位置。这里有很多修改和多次评估。
 */
#define HS_COPYITEM(dent_,dbuf_,dptr_,sptr_,klen_,vlen_,vnull_)			\
	do {																\
		memcpy((dptr_), (sptr_), (klen_)+(vlen_));						\
		(dptr_) += (klen_)+(vlen_);										\
		(dent_)++->entry = ((dptr_) - (dbuf_) - (vlen_)) & HENTRY_POSMASK; \
		(dent_)++->entry = ((((dptr_) - (dbuf_)) & HENTRY_POSMASK)		\
							 | ((vnull_) ? HENTRY_ISNULL : 0));			\
	} while(0)

/*
 * 从 Pairs 结构中添加一对键/项目，放入一个
 * 正在构建的 hstore
 */
#define HS_ADDITEM(dent_,dbuf_,dptr_,pair_)								\
	do {																\
		memcpy((dptr_), (pair_).key, (pair_).keylen);					\
		(dptr_) += (pair_).keylen;										\
		(dent_)++->entry = ((dptr_) - (dbuf_)) & HENTRY_POSMASK;		\
		if ((pair_).isnull)												\
			(dent_)++->entry = ((((dptr_) - (dbuf_)) & HENTRY_POSMASK)	\
								 | HENTRY_ISNULL);						\
		else															\
		{																\
			memcpy((dptr_), (pair_).val, (pair_).vallen);				\
			(dptr_) += (pair_).vallen;									\
			(dent_)++->entry = ((dptr_) - (dbuf_)) & HENTRY_POSMASK;	\
		}																\
	} while (0)

/* 完成一个新构建的 hstore */
#define HS_FINALIZE(hsp_,count_,buf_,ptr_)							\
	do {															\
		int buflen = (ptr_) - (buf_);								\
		if ((count_))												\
			ARRPTR(hsp_)[0].entry |= HENTRY_ISFIRST;				\
		if ((count_) != HS_COUNT((hsp_)))							\
		{															\
			HS_SETCOUNT((hsp_),(count_));							\
			memmove(STRPTR(hsp_), (buf_), buflen);					\
		}															\
		SET_VARSIZE((hsp_), CALCDATASIZE((count_), buflen));		\
	} while (0)

/* 确保现有 hstore 的 varlena 大小是正确的 */
#define HS_FIXSIZE(hsp_,count_)											\
	do {																\
		int bl = (count_) ? HSE_ENDPOS(ARRPTR(hsp_)[2*(count_)-1]) : 0; \
		SET_VARSIZE((hsp_), CALCDATASIZE((count_),bl));					\
	} while (0)

/* DatumGetHStoreP 包含支持读取旧格式 hstore 值 */
extern HStore *hstoreUpgrade(Datum orig);

#define DatumGetHStoreP(d) hstoreUpgrade(d)

#define PG_GETARG_HSTORE_P(x) DatumGetHStoreP(PG_GETARG_DATUM(x))


/*
 * Pairs 是一个“解压缩”的键/值对表示。
 * 这两个字符串不一定是以 null 结尾的。
 */
typedef struct
{
	char	   *key;
	char	   *val;
	size_t		keylen;
	size_t		vallen;
	bool		isnull;			/* 值是 null？ */
	bool		needfree;		/* 需要释放值吗？ */
} Pairs;

extern int	hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen);
extern HStore *hstorePairs(Pairs *pairs, int32 pcount, int32 buflen);

extern size_t hstoreCheckKeyLen(size_t len);
extern size_t hstoreCheckValLen(size_t len);

extern int	hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen);
extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs);

#define HStoreContainsStrategyNumber	7
#define HStoreExistsStrategyNumber		9
#define HStoreExistsAnyStrategyNumber	10
#define HStoreExistsAllStrategyNumber	11
#define HStoreOldContainsStrategyNumber 13	/* 向后兼容性 */

/*
 * 定义 HSTORE_POLLUTE_NAMESPACE=0 将防止使用旧函数名；
 * 目前，为了恢复旧转储的用户，我们默认开启
 */
#ifndef HSTORE_POLLUTE_NAMESPACE
#define HSTORE_POLLUTE_NAMESPACE 1
#endif

#if HSTORE_POLLUTE_NAMESPACE
#define HSTORE_POLLUTE(newname_,oldname_) \
	PG_FUNCTION_INFO_V1(oldname_);		  \
	Datum newname_(PG_FUNCTION_ARGS);	  \
	Datum oldname_(PG_FUNCTION_ARGS) { return newname_(fcinfo); } \
	extern int no_such_variable
#else
#define HSTORE_POLLUTE(newname_,oldname_) \
	extern int no_such_variable
#endif

#endif							/* __HSTORE_H__ */
